package Token

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"go.uber.org/zap"
	"lht-go-plugins/LhtLog"
	"lht-go-plugins/internal/core"
)

// JwtPlugin 配置
type JwtConfig struct {
	SecretKey     string `json:"secret_key"`     // JWT 签名密钥
	ExpireHours   int    `json:"expire_hours"`   // 过期时间（小时）
	LoginEndpoint string `json:"login_endpoint"` // 登录接口路径，默认 /login
	Port          int    `json:"port"`           // HTTP 服务监听端口
}

// 登录请求体
type LoginRequest struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// 登录响应
type LoginResponse struct {
	Token   string `json:"token"`
	Expires int64  `json:"expires_at"`
	Message string `json:"message,omitempty"`
}

type JwtPlugin struct {
	*core.BasePlugin
	conf       JwtConfig
	secretKey  []byte
	httpServer *http.Server
}

func NewJwtPlugin() *JwtPlugin {
	return &JwtPlugin{
		BasePlugin: core.NewBasePlugin("jwt", 1001),
	}
}

func (j *JwtPlugin) Init(cfg any) error {
	conf, ok := cfg.(*JwtConfig)
	if !ok {
		return errors.New("invalid config type: expected *JwtConfig")
	}
	if conf == nil {
		return errors.New("jwt config is nil")
	}
	if conf.SecretKey == "" {
		return errors.New("jwt secret_key cannot be empty")
	}
	if conf.ExpireHours <= 0 {
		conf.ExpireHours = 24 // 默认 24 小时
	}
	if conf.LoginEndpoint == "" {
		conf.LoginEndpoint = "/login"
	}
	if conf.Port <= 0 {
		conf.Port = 8080
	}

	j.conf = *conf
	j.secretKey = []byte(conf.SecretKey)
	return nil
}

func (j *JwtPlugin) Start() error {
	mux := http.NewServeMux()
	mux.HandleFunc(j.conf.LoginEndpoint, j.handleLogin)

	j.httpServer = &http.Server{
		Addr:    fmt.Sprintf(":%d", j.conf.Port),
		Handler: mux,
	}

	go func() {
		LhtLog.Info("JWT Plugin HTTP Server starting", zap.Int("port", j.conf.Port))
		if err := j.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			LhtLog.Error("JWT HTTP Server failed", zap.Error(err))
		}
	}()

	return j.BasePlugin.Start(j.handleData) // 保留 Recv 能力（可选）
}

func (j *JwtPlugin) Stop() error {
	if j.httpServer != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		if err := j.httpServer.Shutdown(ctx); err != nil {
			LhtLog.Error("JWT HTTP Server shutdown failed", zap.Error(err))
			return err
		}
	}
	return j.BasePlugin.Stop()
}

// Recv 接收其他插件发来的数据（可选用于内部通信）
func (j *JwtPlugin) handleData(data []byte) {
	fmt.Printf("[jwt] recv internal data: %s\n", string(data))
}

// === 登录处理 ===
func (j *JwtPlugin) handleLogin(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req LoginRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		j.respondJSON(w, http.StatusBadRequest, LoginResponse{Message: "Invalid JSON"})
		return
	}

	// === 用户验证逻辑（这里你需要替换成真实的验证方式）===
	// 例如：数据库查用户、LDAP、配置文件等
	if !j.validateUser(req.Username, req.Password) {
		j.respondJSON(w, http.StatusUnauthorized, LoginResponse{Message: "Invalid username or password"})
		return
	}

	// === 生成 JWT ===
	expirationTime := time.Now().Add(time.Duration(j.conf.ExpireHours) * time.Hour)
	claims := &jwt.RegisteredClaims{
		Subject:   req.Username,
		ExpiresAt: jwt.NewNumericDate(expirationTime),
		IssuedAt:  jwt.NewNumericDate(time.Now()),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(j.secretKey)
	if err != nil {
		j.respondJSON(w, http.StatusInternalServerError, LoginResponse{Message: "Failed to generate token"})
		return
	}

	// === 响应 ===
	j.respondJSON(w, http.StatusOK, LoginResponse{
		Token:   tokenString,
		Expires: expirationTime.Unix(),
	})
}

// === 工具函数 ===

// 模拟用户验证（请替换为真实逻辑！）
func (j *JwtPlugin) validateUser(username, password string) bool {
	// 示例：硬编码用户（生产请用数据库）
	users := map[string]string{
		"admin": "123456",
		"user":  "password",
	}
	pwd, ok := users[username]
	return ok && pwd == password
}

// JSON 响应封装
func (j *JwtPlugin) respondJSON(w http.ResponseWriter, status int, payload LoginResponse) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(payload)
}
